package jinyilw.swing.rlaf.ui;

import jinyilw.swing.ColorTools;
import jinyilw.swing.ComponentTools;
import jinyilw.swing.FontTools;
import jinyilw.swing.rlaf.theme.RThemeColor;

import javax.swing.JComponent;
import javax.swing.JTable;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicTableUI;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;

/**
 * @author Jinyi
 */
public class RTableUI extends BasicTableUI
{

	public static ComponentUI createUI(JComponent c)
	{
		return new RTableUI();
	}

	@Override
	public void installUI(JComponent c)
	{
		super.installUI(c);
		c.setOpaque(false);
		c.setBackground(ColorTools.transparentColor);
		int height = ComponentTools
				.getFontMetrics(table, null, FontTools.defaultFont).getHeight();
		table.setRowHeight(height + height / 4);
		table.setSelectionBackground(RThemeColor.darkThemeColor);
		table.setSelectionForeground(Color.white);
	}

	@Override
	public void update(Graphics g, JComponent c)
	{
		if (table.getSelectionBackground() != RThemeColor.darkThemeColor)
			table.setSelectionBackground(RThemeColor.darkThemeColor);
		RThemeColor.customForegroundSetting(c);
		paint(g, c);
	}

	@Override
	public void paint(Graphics g, JComponent c)
	{
		Rectangle clip = g.getClipBounds();
		Rectangle bounds = table.getBounds();
		bounds.x = bounds.y = 0;

		if (table.getRowCount() <= 0 || table.getColumnCount() <= 0
				|| !bounds.intersects(clip))
		{
			paintDropLines(g);
			return;
		}

		boolean ltr = table.getComponentOrientation().isLeftToRight();

		Point upperLeft = clip.getLocation();
		Point lowerRight = new Point(clip.x + clip.width - 1,
				clip.y + clip.height - 1);

		int rMin = table.rowAtPoint(upperLeft);
		int rMax = table.rowAtPoint(lowerRight);

		if (rMin == -1)
			rMin = 0;
		if (rMax == -1)
			rMax = table.getRowCount() - 1;

		int cMin = table.columnAtPoint(ltr ? upperLeft : lowerRight);
		int cMax = table.columnAtPoint(ltr ? lowerRight : upperLeft);
		if (cMin == -1)
			cMin = 0;
		if (cMax == -1)
			cMax = table.getColumnCount() - 1;
		paintGrid(g, rMin, rMax, cMin, cMax);
		paintCells(g, rMin, rMax, cMin, cMax);
		paintDropLines(g);
	}

	void paintGrid(Graphics g, int rMin, int rMax, int cMin, int cMax)
	{
		g.setColor(table.getGridColor());
		Rectangle minCell = table.getCellRect(rMin, cMin, true);
		Rectangle maxCell = table.getCellRect(rMax, cMax, true);
		Rectangle damagedArea = minCell.union(maxCell);

		if (table.getShowHorizontalLines())
		{
			int tableWidth = damagedArea.x + damagedArea.width;
			int y = damagedArea.y;
			for (int row = rMin; row <= rMax; row++)
			{
				y += table.getRowHeight(row);
				g.drawLine(damagedArea.x, y - 1, tableWidth - 1, y - 1);
			}
		}
		if (table.getShowVerticalLines())
		{
			TableColumnModel cm = table.getColumnModel();
			int tableHeight = damagedArea.y + damagedArea.height;
			int x;
			if (table.getComponentOrientation().isLeftToRight())
			{
				x = damagedArea.x;
				for (int column = cMin; column <= cMax; column++)
				{
					int w = cm.getColumn(column).getWidth();
					g.drawLine(x, 0, x, tableHeight - 1);
					x += w;
				}
				g.drawLine(x - 1, 0, x - 1, tableHeight - 1);
			} else
			{
				x = damagedArea.x;
				for (int column = cMax; column >= cMin; column--)
				{
					int w = cm.getColumn(column).getWidth();
					g.drawLine(x, 0, x, tableHeight - 1);
					x += w;
				}
				g.drawLine(x - 1, 0, x - 1, tableHeight - 1);
			}
		}
	}

	void paintCells(Graphics g, int rMin, int rMax, int cMin, int cMax)
	{
		JTableHeader header = table.getTableHeader();
		TableColumn draggedColumn = (header == null)
				? null
				: header.getDraggedColumn();

		TableColumnModel cm = table.getColumnModel();
		int columnMargin = cm.getColumnMargin();

		Rectangle cellRect;
		TableColumn aColumn;
		int columnWidth;
		if (table.getComponentOrientation().isLeftToRight())
		{
			for (int row = rMin; row <= rMax; row++)
			{
				cellRect = table.getCellRect(row, cMin, false);
				for (int column = cMin; column <= cMax; column++)
				{
					aColumn = cm.getColumn(column);
					columnWidth = aColumn.getWidth();
					cellRect.width = columnWidth - columnMargin;
					if (aColumn != draggedColumn)
						paintCell(g, cellRect, row, column);
					cellRect.x += columnWidth;
				}
			}
		} else
		{
			for (int row = rMin; row <= rMax; row++)
			{
				cellRect = table.getCellRect(row, cMin, false);
				aColumn = cm.getColumn(cMin);
				if (aColumn != draggedColumn)
				{
					columnWidth = aColumn.getWidth();
					cellRect.width = columnWidth - columnMargin;
					paintCell(g, cellRect, row, cMin);
				}
				for (int column = cMin + 1; column <= cMax; column++)
				{
					aColumn = cm.getColumn(column);
					columnWidth = aColumn.getWidth();
					cellRect.width = columnWidth - columnMargin;
					cellRect.x -= columnWidth;
					if (aColumn != draggedColumn)
					{
						paintCell(g, cellRect, row, column);
					}
				}
			}
		}
		rendererPane.removeAll();
	}

	void paintCell(Graphics g, Rectangle cellRect, int row, int column)
	{
		if (table.isEditing() && table.getEditingRow() == row
				&& table.getEditingColumn() == column)
		{
			Component component = table.getEditorComponent();
			component.setBounds(cellRect);
			component.validate();
		} else
		{
			TableCellRenderer renderer = table.getCellRenderer(row, column);
			Component component = table.prepareRenderer(renderer, row, column);
			rendererPane.paintComponent(g, component, null, cellRect.x,
					cellRect.y, cellRect.width, cellRect.height, true);
		}
	}

	void paintDropLines(Graphics g)
	{
		JTable.DropLocation loc = table.getDropLocation();
		if (loc == null)
		{
			return;
		}

		Color color = UIManager.getColor("Table.dropLineColor");
		Color shortColor = UIManager.getColor("Table.dropLineShortColor");
		if (color == null && shortColor == null)
		{
			return;
		}

		Rectangle rect;

		rect = getHDropLineRect(loc);
		if (rect != null)
		{
			int x = rect.x;
			int w = rect.width;
			if (color != null)
			{
				extendRect(rect, true);
				g.setColor(color);
				g.fillRect(rect.x, rect.y, rect.width, rect.height);
			}
			if (!loc.isInsertColumn() && shortColor != null)
			{
				g.setColor(shortColor);
				g.fillRect(x, rect.y, w, rect.height);
			}
		}

		rect = getVDropLineRect(loc);
		if (rect != null)
		{
			int y = rect.y;
			int h = rect.height;
			if (color != null)
			{
				extendRect(rect, false);
				g.setColor(color);
				g.fillRect(rect.x, rect.y, rect.width, rect.height);
			}
			if (!loc.isInsertRow() && shortColor != null)
			{
				g.setColor(shortColor);
				g.fillRect(rect.x, y, rect.width, h);
			}
		}
	}

	Rectangle getHDropLineRect(JTable.DropLocation loc)
	{
		if (!loc.isInsertRow())
		{
			return null;
		}

		int row = loc.getRow();
		int col = loc.getColumn();
		if (col >= table.getColumnCount())
		{
			col--;
		}

		Rectangle rect = table.getCellRect(row, col, true);

		if (row >= table.getRowCount())
		{
			row--;
			Rectangle prevRect = table.getCellRect(row, col, true);
			rect.y = prevRect.y + prevRect.height;
		}

		if (rect.y == 0)
		{
			rect.y = -1;
		} else
		{
			rect.y -= 2;
		}

		rect.height = 3;

		return rect;
	}

	Rectangle getVDropLineRect(JTable.DropLocation loc)
	{
		if (!loc.isInsertColumn())
		{
			return null;
		}

		boolean ltr = table.getComponentOrientation().isLeftToRight();
		int col = loc.getColumn();
		Rectangle rect = table.getCellRect(loc.getRow(), col, true);

		if (col >= table.getColumnCount())
		{
			col--;
			rect = table.getCellRect(loc.getRow(), col, true);
			if (ltr)
			{
				rect.x = rect.x + rect.width;
			}
		} else if (!ltr)
		{
			rect.x = rect.x + rect.width;
		}

		if (rect.x == 0)
		{
			rect.x = -1;
		} else
		{
			rect.x -= 2;
		}

		rect.width = 3;

		return rect;
	}

	Rectangle extendRect(Rectangle rect, boolean horizontal)
	{
		if (rect == null)
		{
			return rect;
		}

		if (horizontal)
		{
			rect.x = 0;
			rect.width = table.getWidth();
		} else
		{
			rect.y = 0;

			if (table.getRowCount() != 0)
			{
				Rectangle lastRect = table.getCellRect(table.getRowCount() - 1,
						0, true);
				rect.height = lastRect.y + lastRect.height;
			} else
			{
				rect.height = table.getHeight();
			}
		}

		return rect;
	}
}
